// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/protobuf-expectation/expectation-eval.h"

#include <stddef.h>

#include "google/protobuf-expectation/stubs/integral_types.h"
#include "glog/logging.h"
#include "google/protobuf-expectation/expectation.pb.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/message.h"
#include "google/protobuf-expectation/stubs/case.h"
#include "google/protobuf-expectation/stubs/numbers.h"
#include "google/protobuf-expectation/stubs/mathutil.h"
#include "re2/re2.h"

using std::string;
using std::vector;

using google::protobuf::EnumDescriptor;
using google::protobuf::EnumValueDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::Message;
using google::protobuf::Reflection;

namespace google {
namespace protobuf_expectation {
namespace {
// These safe_strtox and SimpleXtoa are called from templatized Evaluate() and
// its helper functions.
bool safe_strtox(const string& str, bool* value) {
  CHECK_NOTNULL(value);
  if (str == "1" || str == "TRUE" || str == "True" || str == "true") {
    *value = true;
    return true;
  }
  if (str == "0" || str == "FALSE" || str == "False" || str == "false") {
    *value = false;
    return true;
  }
  return false;
}
inline bool safe_strtox(const string& str, int32* value) {
  return safe_strto32(str, value);
}
inline bool safe_strtox(const string& str, uint32* value) {
  return safe_strtou32(str, value);
}
inline bool safe_strtox(const string& str, int64* value) {
  return safe_strto64(str, value);
}
inline bool safe_strtox(const string& str, uint64* value) {
  return safe_strtou64(str, value);
}
inline bool safe_strtox(const string& str, float* value) {
  return safe_strtof(str, value);
}
inline bool safe_strtox(const string& str, double* value) {
  return safe_strtod(str, value);
}

template<typename T>
inline string SimpleXtoa(T value) {
  return SimpleItoa(value);
}
template<> inline string SimpleXtoa<bool>(bool value) {
  return value ? "true" : "false";
}
template<> inline string SimpleXtoa<float>(float value) {
  return SimpleFtoa(value);
}
template<> inline string SimpleXtoa<double>(double value) {
  return SimpleDtoa(value);
}
template<> inline string SimpleXtoa<string>(string value) {
  return value;
}
template<> inline string SimpleXtoa<const EnumValueDescriptor*>(
    const EnumValueDescriptor* value) {
  return value->name() + " (" + SimpleXtoa(value->number()) + ")";
}

// A helper function to return a field name for formatting EvaluationResult.
inline string FieldName(const FieldDescriptor* field, int index) {
  return field->name() + ((index < 0) ? "" : "[" + SimpleXtoa(index) + "]");
}

// A helper function for formatting EvaluationResult:
// Concatenates EvaluationResult.expected for those succeeded in the
// eval_results while prepending the prefix and appending postfix to
// each expected string.
void AppendSucceededExpected(
    const vector<EvaluationResult>& eval_results,
    const string& prefix, const string& postfix, string* str) {
  for (int i = 0; i < eval_results.size(); ++i) {
    if (eval_results[i].success) {
      *str += prefix + eval_results[i].expected + postfix;
    }
  }
}

// A helper function for formatting EvaluationResult:
// Concatenates EvaluationResult.expected for those failed in the eval_results
// while prepending the prefix and appending postfix to each expected string.
void AppendFailedExpected(
    const vector<EvaluationResult>& eval_results,
    const string& prefix, const string& postfix, string* str) {
  for (int i = 0; i < eval_results.size(); ++i) {
    if (!eval_results[i].success) {
      *str += prefix + eval_results[i].expected + postfix;
    }
  }
}

template<typename T>
string DebugString(T expectation) {
  if (expectation.has_debug_string()) {
    return expectation.debug_string();
  } else {
    return expectation.Utf8DebugString();
  }
}

template<> string DebugString<ValueExpectation>(ValueExpectation expectation) {
  if (expectation.has_debug_string()) {
    return expectation.debug_string();
  }
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  const bool has_value = expectation.has_value();
  const int value_expectation_size = expectation.value_expectation_size();
  switch (op) {
    case ValueExpectation::EQ:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value == " + expectation.value();
    case ValueExpectation::NE:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value != " + expectation.value();
    case ValueExpectation::LE:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value <= " + expectation.value();
    case ValueExpectation::LT:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value < " + expectation.value();
    case ValueExpectation::GE:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value >= " + expectation.value();
    case ValueExpectation::GT:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value > " + expectation.value();
    case ValueExpectation::ALMOST_EQ:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "value =~ " + expectation.value();
    case ValueExpectation::FULL_MATCH_RE:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "full match with re \"" + expectation.value() + "\"";
    case ValueExpectation::PARTIAL_MATCH_RE:
      if (!has_value || value_expectation_size > 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "partial match with re \"" + expectation.value() + "\"";
    case ValueExpectation::STRING_LENGTH:
      if (has_value || value_expectation_size != 1) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "string length " + DebugString(expectation.value_expectation(0));
    case ValueExpectation::IS:
      if (has_value || value_expectation_size != 1) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "(" + DebugString(expectation.value_expectation(0)) + ")";
    case ValueExpectation::IS_NOT:
      if (has_value || value_expectation_size != 1) {
        return "invalid expectation: " + expectation.DebugString();
      }
      return "!(" + DebugString(expectation.value_expectation(0)) + ")";
    case ValueExpectation::AND:
      if (has_value || value_expectation_size == 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      {
        string s;
        for (int i = 0; i < expectation.value_expectation_size(); ++i) {
          if (i > 0) {
            s += " && ";
          }
          s += "(" + DebugString(expectation.value_expectation(i)) + ")";
        }
        return s;
      }
    case ValueExpectation::OR:
      if (has_value || value_expectation_size == 0) {
        return "invalid expectation: " + expectation.DebugString();
      }
      {
        string s;
        for (int i = 0; i < expectation.value_expectation_size(); ++i) {
          if (i > 0) {
            s += " || ";
          }
          s += "(" + DebugString(expectation.value_expectation(i)) + ")";
        }
        return s;
      }
    default:
      LOG(FATAL) << "Should not reach here.";
  }
  return "invalid expectation: " + expectation.DebugString();
}

void AddEvalResult(const string& field, const string& expected,
                   const string& actual, bool success,
                   vector<EvaluationResult>* eval_results) {
  if (eval_results != NULL) {
    eval_results->push_back(EvaluationResult());
    eval_results->back().field = field;
    eval_results->back().expected = expected;
    eval_results->back().actual = actual;
    eval_results->back().success = success;
  }
}
}  // namespace

// TODO(user): support extensions.
bool Evaluate(const FieldExpectation& expectation,
              const Message& message,
              vector<EvaluationResult>* eval_results) {
  // Check if field exists.
  const FieldDescriptor* field =
      message.GetDescriptor()->FindFieldByName(expectation.field_name());
  if (field == NULL) {
    AddEvalResult(expectation.field_name(),
                  "message type " + message.GetTypeName() + " has the field",
                  "field not defined", false, eval_results);
    return false;
  }
  const bool is_repeated = field->is_repeated();
  const bool is_message =
      (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
  const Reflection* reflection = message.GetReflection();
  bool result = true;
  bool evaluated = false;
  if (!is_repeated) {
    if (expectation.has_field_size()) {
      AddEvalResult(expectation.field_name(), DebugString(expectation),
                    "the field is not repeated but field_size is specified",
                    false, eval_results);
      return false;
    }
    if (expectation.has_has_field()) {
      const bool has_field = reflection->HasField(message, field);
      result = (expectation.has_field() == has_field);
      AddEvalResult(expectation.field_name(),
                    "has_field: " + SimpleXtoa(expectation.has_field()),
                    "has_field: " + SimpleXtoa(has_field),
                    result, eval_results);
      evaluated = true;
    }
  } else if (is_repeated) {
    if (expectation.has_has_field()) {
      AddEvalResult(expectation.field_name(), DebugString(expectation),
                    "the field is repeated but has_field is specified",
                    false, eval_results);
      return false;
    }
    if (expectation.has_field_size()) {
      const int field_size = reflection->FieldSize(message, field);
      result = Evaluate(expectation.field_size(),
                        static_cast<int32>(field_size), NULL);
      AddEvalResult(expectation.field_name(),
                    "field_size: " + DebugString(expectation.field_size()),
                    "field_size: " + SimpleItoa(field_size),
                    result, eval_results);
      evaluated = true;
    }
  }
  vector<EvaluationResult> tmp_eval_results;
  if (!is_repeated && !is_message) {
    if (expectation.has_value_expectation()) {
      // We don't test reflection->HasField(message, field) here.
      // Evaluate() will work with the default value, and if a user really
      // want to check whether a value is set or not s/he can do so with
      // FieldExpectation.has_field.
      result = Evaluate(expectation.value_expectation(), message, field, -1,
                        eval_results);
      evaluated = true;
    }
  } else if (!is_repeated && is_message) {
    if (expectation.has_message_expectation()) {
      result = Evaluate(expectation.message_expectation(),
                        reflection->GetMessage(message, field),
                        &tmp_eval_results);
      for (int i = 0; i < tmp_eval_results.size(); ++i) {
        AddEvalResult(field->name() + "." + tmp_eval_results[i].field,
                      tmp_eval_results[i].expected, tmp_eval_results[i].actual,
                      tmp_eval_results[i].success, eval_results);
      }
      evaluated = true;
    }
  } else if (is_repeated && !is_message) {
    if (expectation.has_repeated_value_expectation()) {
      result = Evaluate(expectation.repeated_value_expectation(), message,
                        field, eval_results);
      evaluated = true;
    }
  } else if (is_repeated && is_message) {
    if (expectation.has_repeated_message_expectation()) {
      result = Evaluate(expectation.repeated_message_expectation(), message,
                        field, eval_results);
      evaluated = true;
    }
  }
  if (!evaluated) {
    result = false;
    AddEvalResult("", DebugString(expectation), "invalid expectation", false,
                  eval_results);
  }
  return result;
}

bool Evaluate(const ValueExpectation& expectation,
              const Message& message,
              const FieldDescriptor* field,
              int index,
              vector<EvaluationResult>* eval_results) {
  CHECK_NOTNULL(field);
  const bool is_repeated = field->is_repeated();
  if (!is_repeated && index != -1) {
    AddEvalResult(FieldName(field, index), DebugString(expectation),
                  "the field is not repeated but index is specified",
                  false, eval_results);
    return false;
  }
  if (is_repeated && index < 0) {
    AddEvalResult(FieldName(field, index), DebugString(expectation),
                  "the field is repeated but index is not specified",
                  false, eval_results);
    return false;
  }
  vector<EvaluationResult> tmp_eval_results;
  bool result = false;
  const Reflection* reflection = message.GetReflection();
  if (!is_repeated) {
    switch (field->cpp_type()) {
      case FieldDescriptor::CPPTYPE_BOOL:
        result = Evaluate(expectation, reflection->GetBool(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_INT32:
        result = Evaluate(expectation, reflection->GetInt32(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_UINT32:
        result = Evaluate(expectation, reflection->GetUInt32(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_INT64:
        result = Evaluate(expectation, reflection->GetInt64(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_UINT64:
        result = Evaluate(expectation, reflection->GetUInt64(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_FLOAT:
        result = Evaluate(expectation, reflection->GetFloat(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_DOUBLE:
        result = Evaluate(expectation, reflection->GetDouble(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_STRING:
        result = Evaluate(expectation, reflection->GetString(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_ENUM:
        result = Evaluate(expectation, reflection->GetEnum(message, field),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_MESSAGE:
        AddEvalResult(FieldName(field, index), DebugString(expectation),
                      "invalid type of a message field", false, eval_results);
        return false;
      default:
        LOG(FATAL) << "Should not reach here. Unknown C++ data type of "
                   << "a message field: " << field->DebugString();
    }
  } else {
    switch (field->cpp_type()) {
      case FieldDescriptor::CPPTYPE_BOOL:
        result = Evaluate(expectation,
                          reflection->GetRepeatedBool(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_INT32:
        result = Evaluate(expectation,
                          reflection->GetRepeatedInt32(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_UINT32:
        result = Evaluate(expectation,
                          reflection->GetRepeatedUInt32(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_INT64:
        result = Evaluate(expectation,
                          reflection->GetRepeatedInt64(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_UINT64:
        result = Evaluate(expectation,
                          reflection->GetRepeatedUInt64(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_FLOAT:
        result = Evaluate(expectation,
                          reflection->GetRepeatedFloat(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_DOUBLE:
        result = Evaluate(expectation,
                          reflection->GetRepeatedDouble(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_STRING:
        result = Evaluate(expectation,
                          reflection->GetRepeatedString(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_ENUM:
        result = Evaluate(expectation,
                          reflection->GetRepeatedEnum(message, field, index),
                          &tmp_eval_results);
        break;
      case FieldDescriptor::CPPTYPE_MESSAGE:
        AddEvalResult(FieldName(field, index), DebugString(expectation),
                      "invalid type of a message field", false, eval_results);
        return false;
      default:
        LOG(FATAL) << "Should not reach here. Unknown C++ data type of "
                   << "a message field: " << field->DebugString();
    }
  }
  if (tmp_eval_results.size() == 0) {
    AddEvalResult(FieldName(field, index), DebugString(expectation),
                  message.DebugString(), result, eval_results);
    LOG(ERROR) << "Evaluate() of the field type should return an eval result.";
  } else {
    AddEvalResult(FieldName(field, index), DebugString(expectation),
                  tmp_eval_results[0].actual, result, eval_results);
  }
  return result;
}

namespace {
template<typename T>
bool MaybeProcessCombineOperators(
    const ValueExpectation& expectation, T value,
    bool* result, vector<EvaluationResult>* eval_results) {
  CHECK_NOTNULL(result);
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  vector<EvaluationResult> tmp_eval_results;
  const string expected = DebugString(expectation);
  string actual = SimpleXtoa(value);
  if (op == ValueExpectation::IS) {
    if (expectation.value_expectation_size() != 1) {
      AddEvalResult("", expected, "invalid expectation", false, eval_results);
    } else {
      *result = Evaluate(expectation.value_expectation(0), value,
                         &tmp_eval_results);
      if (!*result) {
        AppendFailedExpected(
            tmp_eval_results, "\n(failure with expectation: ", ")", &actual);
      }
      AddEvalResult("", expected, actual, *result, eval_results);
    }
    return true;
  } else if (op == ValueExpectation::IS_NOT) {
    if (expectation.value_expectation_size() != 1) {
      AddEvalResult("", expected, "invalid expectation", false, eval_results);
    } else {
      *result = !Evaluate(expectation.value_expectation(0), value,
                          &tmp_eval_results);
      if (!*result) {
        AppendSucceededExpected(
            tmp_eval_results, "\n(failure with expectation: ", ")", &actual);
      }
      AddEvalResult("", expected, actual, *result, eval_results);
    }
    return true;
  } else if (op == ValueExpectation::AND) {
    if (expectation.value_expectation_size() == 0) {
      AddEvalResult("", expected, "invalid expectation", false, eval_results);
    } else {
      *result = true;
      for (int i = 0; i < expectation.value_expectation_size(); ++i) {
        tmp_eval_results.clear();
        if (!Evaluate(expectation.value_expectation(i), value,
                      &tmp_eval_results)) {
          *result = false;
          AppendFailedExpected(
              tmp_eval_results, "\n(failure with expectation: ", ")", &actual);
          break;
        }
      }
      AddEvalResult("", expected, actual, *result, eval_results);
    }
    return true;
  } else if (op == ValueExpectation::OR) {
    if (expectation.value_expectation_size() == 0) {
      AddEvalResult("", expected, "invalid expectation", false, eval_results);
    } else {
      *result = false;
      for (int i = 0; i < expectation.value_expectation_size(); ++i) {
        tmp_eval_results.clear();
        if (Evaluate(expectation.value_expectation(i), value,
                     &tmp_eval_results)) {
          *result = true;
          AppendSucceededExpected(
              tmp_eval_results, "\n(success with expectation: ", ")", &actual);
          break;
        }
      }
      AddEvalResult("", expected, actual, *result, eval_results);
    }
    return true;
  }
  return false;
}
}  // namespace

template<typename T>
bool Evaluate(const ValueExpectation& expectation, T value,
              vector<EvaluationResult>* eval_results) {
  bool result = false;
  if (MaybeProcessCombineOperators(expectation, value,
                                   &result, eval_results)) {
    return result;
  }
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  if (op < ValueExpectation::EQ || op > ValueExpectation::ALMOST_EQ) {
    AddEvalResult("", DebugString(expectation),
                  "invalid operator in the expectation: " +
                  ValueExpectation::Operator_Name(op), false, eval_results);
    return false;
  }
  T expected;
  if (!safe_strtox(expectation.value(), &expected)) {
    AddEvalResult("", DebugString(expectation),
                  "invalid value in the expectation: " +
                  expectation.value(), false, eval_results);
    return false;
  }
  if (expectation.value_expectation_size() > 0) {
    AddEvalResult("", DebugString(expectation),
                  "invalid value_expectation in the expectation",
                  false, eval_results);
    return false;
  }
  switch (op) {
    case ValueExpectation::EQ:
      result = expected == value;
      break;
    case ValueExpectation::NE:
      result = expected != value;
      break;
    case ValueExpectation::LE:
      result = expected >= value;
      break;
    case ValueExpectation::LT:
      result = expected > value;
      break;
    case ValueExpectation::GE:
      result = expected <= value;
      break;
    case ValueExpectation::GT:
      result = expected < value;
      break;
    case ValueExpectation::ALMOST_EQ:
      // AlmostEquals() works for integers as well as float and double.
      result = MathUtil::AlmostEquals(expected, value);
      break;
    default:
      LOG(FATAL) << "Should not reach here.";
  }
  AddEvalResult("", DebugString(expectation), SimpleXtoa(value), result,
                eval_results);
  return result;
}

template<>
bool Evaluate<bool>(const ValueExpectation& expectation, bool value,
                    vector<EvaluationResult>* eval_results) {
  bool result = false;
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  if (op != ValueExpectation::EQ && op != ValueExpectation::NE) {
    AddEvalResult("", DebugString(expectation),
                  "invalid operator in the expectation: " +
                  ValueExpectation::Operator_Name(op), false, eval_results);
    return false;
  }
  bool expected = true;
  if (!safe_strtox(expectation.value(), &expected)) {
    AddEvalResult("", DebugString(expectation),
                  "invalid bool value in the expectation: " +
                  expectation.value(), false, eval_results);
    return false;
  }
  if (expectation.value_expectation_size() > 0) {
    AddEvalResult("", DebugString(expectation),
                  "invalid value_expectation in the expectation",
                  false, eval_results);
    return false;
  }
  switch (op) {
    case ValueExpectation::EQ:
      result = expected == value;
      break;
    case  ValueExpectation::NE:
      result = expected != value;
      break;
    default:
      LOG(FATAL) << "Should not reach here.";
  }
  AddEvalResult("", DebugString(expectation), SimpleXtoa(value), result,
                eval_results);
  return result;
}

template<>
bool Evaluate<const EnumValueDescriptor*>(
    const ValueExpectation& expectation,
    const EnumValueDescriptor* value,
    vector<EvaluationResult>* eval_results) {
  CHECK_NOTNULL(value);
  bool result = false;
  if (MaybeProcessCombineOperators(expectation, value,
                                   &result, eval_results)) {
    return result;
  }
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  if (op < ValueExpectation::EQ || op > ValueExpectation::GT) {
    AddEvalResult("", DebugString(expectation),
                  "invalid operator in the expectation: " +
                  ValueExpectation::Operator_Name(op),
                  false, eval_results);
    return false;
  }
  const EnumDescriptor* enum_descriptor = value->type();
  const EnumValueDescriptor* expected =
      enum_descriptor->FindValueByName(expectation.value());
  if (expected == NULL) {
    int number;
    if (safe_strto32(expectation.value(), &number)) {
      expected = enum_descriptor->FindValueByNumber(number);
    }
    if (expected == NULL) {
      AddEvalResult("", DebugString(expectation),
                    "invalid enum value in the expectation: " +
                    expectation.value(),
                    false, eval_results);
      return false;
    }
  }
  if (expectation.value_expectation_size() > 0) {
    AddEvalResult("", DebugString(expectation),
                  "invalid value_expectation in the expectation",
                  false, eval_results);
    return false;
  }
  switch (op) {
    case ValueExpectation::EQ:
      result = expected->number() == value->number();
      break;
    case ValueExpectation::NE:
      result = expected->number() != value->number();
      break;
    case ValueExpectation::LE:
      result = expected->number() >= value->number();
      break;
    case ValueExpectation::LT:
      result = expected->number() > value->number();
      break;
    case ValueExpectation::GE:
      result = expected->number() <= value->number();
      break;
    case ValueExpectation::GT:
      result = expected->number() < value->number();
      break;
    default:
      LOG(FATAL) << "Should not reach here.";
  }
  AddEvalResult("", DebugString(expectation), SimpleXtoa(value), result,
                eval_results);
  return result;
}

bool Evaluate(const ValueExpectation& expectation,
              const string& value,
              vector<EvaluationResult>* eval_results) {
  bool result = false;
  if (MaybeProcessCombineOperators(expectation, value,
                                   &result, eval_results)) {
    return result;
  }
  // Proto field names of C++ reserved keywords are appended with "_".
  const ValueExpectation::Operator op = expectation.operator_();
  const string expected = DebugString(expectation);
  if (op != ValueExpectation::EQ && op != ValueExpectation::NE &&
      (op < ValueExpectation::STRING_LENGTH ||
       op > ValueExpectation::PARTIAL_MATCH_RE)) {
    AddEvalResult("", expected, "invalid operator in the expectation: " +
                  ValueExpectation::Operator_Name(op), false, eval_results);
    return false;
  }
  if ((op != ValueExpectation::STRING_LENGTH && !expectation.has_value()) ||
      (op == ValueExpectation::STRING_LENGTH && expectation.has_value())) {
    AddEvalResult("", expected, "invalid value in the expectation",
                  false, eval_results);
    return false;
  }
  if ((op != ValueExpectation::STRING_LENGTH &&
       expectation.value_expectation_size() > 0) ||
      (op == ValueExpectation::STRING_LENGTH &&
       expectation.value_expectation_size() != 1)) {
    AddEvalResult("", expected, "invalid value_expectation in the expectation",
                  false, eval_results);
    return false;
  }
  // It's not necessarily clear what's the best way to define order of strings,
  // so LE, LT, GE, GT are not (yet) supported here.
  switch (op) {
    case ValueExpectation::EQ:
      if (expectation.case_sensitive()) {
        result = (expectation.value() == value);
      } else {
        result = StringCaseEqual(expectation.value(), value);
      }
      break;
    case ValueExpectation::NE:
      if (expectation.case_sensitive()) {
        result = (expectation.value() != value);
      } else {
        result = !StringCaseEqual(expectation.value(), value);
      }
      break;
    case ValueExpectation::STRING_LENGTH:
      {
        vector<EvaluationResult> tmp_eval_results;
        result = Evaluate(expectation.value_expectation(0),
                          static_cast<int32>(value.length()),
                          &tmp_eval_results);
        string actual = SimpleXtoa(value.length());
        if (!result) {
          AppendFailedExpected(
              tmp_eval_results, "\n(failure with expectation: ", ")", &actual);
        }
        AddEvalResult("", expected, actual, result, eval_results);
      }
      break;
    case ValueExpectation::FULL_MATCH_RE:
      {
        RE2::Options options;
        options.set_case_sensitive(expectation.case_sensitive());
        RE2 re(expectation.value(), options);
        result = RE2::FullMatch(value, re);
      }
      break;
    case ValueExpectation::PARTIAL_MATCH_RE:
      {
        RE2::Options options;
        options.set_case_sensitive(expectation.case_sensitive());
        RE2 re(expectation.value(), options);
        result = RE2::PartialMatch(value, re);
      }
      break;
    default:
      LOG(FATAL) << "Should not reach here.";
  }
  if (op != ValueExpectation::STRING_LENGTH) {
    AddEvalResult("", expected, SimpleXtoa(value), result, eval_results);
  }
  return result;
}

bool Evaluate(const MessageExpectation& expectation,
              const Message& message,
              vector<EvaluationResult>* eval_results) {
  bool result = true;
  for (int i = 0; i < expectation.field_expectation_size(); ++i) {
    if (!Evaluate(expectation.field_expectation(i), message, eval_results)) {
      result = false;
    }
  }
  return result;
}

bool Evaluate(const RepeatedValueExpectation& expectation,
              const Message& message,
              const FieldDescriptor* field,
              vector<EvaluationResult>* eval_results) {
  const Reflection* reflection = message.GetReflection();
  const int field_size = reflection->FieldSize(message, field);
  vector<EvaluationResult> tmp_eval_results;
  bool result = true;
  if (expectation.has_for_each()) {
    const string expected = "for_each: " +
        DebugString(expectation.for_each().value_expectation());
    bool for_each_result = true;
    if (field_size == 0) {
      AddEvalResult(field->name(), expected,
                    "no value in the repeated field", true, eval_results);
    } else {
      for (int i = 0; i < field_size; ++i) {
        tmp_eval_results.clear();
        if (!Evaluate(expectation.for_each().value_expectation(),
                      message, field, i, &tmp_eval_results)) {
          for_each_result = false;
          AddEvalResult(field->name(), expected,
                      tmp_eval_results[0].actual + "\n" +  // TOOD?
                        "at_index: " + SimpleXtoa(i), false, eval_results);
        }
      }
      if (for_each_result) {
        AddEvalResult(field->name(), expected,
                      "all good", true, eval_results);
      }
    }
    result = result && for_each_result;
  }
  for (int i = 0; i < expectation.have_size(); ++i) {
    string expected = "have: " +
        DebugString(expectation.have(i).value_expectation());
    if (expectation.have(i).has_at_index()) {
      expected += "\nat_index: " + DebugString(expectation.have(i).at_index());
    }
    bool have_result = false;
    for (int j = 0; j < field_size; ++j) {
      tmp_eval_results.clear();
      if (Evaluate(expectation.have(i).value_expectation(),
                   message, field, j, &tmp_eval_results)) {
        if (expectation.have(i).has_at_index()) {
          if (Evaluate(expectation.have(i).at_index(), static_cast<int32>(j),
                       NULL)) {
            have_result = true;
            AddEvalResult(field->name(), expected,
                          tmp_eval_results[0].actual + "\n" +
                          "at_index: " + SimpleXtoa(j), true, eval_results);
          }
        } else {
          have_result = true;
          AddEvalResult(field->name(), expected,
                        tmp_eval_results[0].actual + "\n" +
                        "at_index: " + SimpleXtoa(j), true, eval_results);
        }
      }
    }
    if (!have_result) {
      AddEvalResult(field->name(), expected,
                    string("value not found") +
                    (expectation.have(i).has_at_index() ? " at the index" : ""),
                    false, eval_results);
    }
    result = result && have_result;
  }
  for (int i = 0; i < expectation.do_not_have_size(); ++i) {
    const string expected = "do_not_have: " +
        DebugString(expectation.do_not_have(i).value_expectation());
    bool do_not_have_result = true;
    for (int j = 0; j < field_size; ++j) {
      tmp_eval_results.clear();
      if (Evaluate(expectation.do_not_have(i).value_expectation(),
                   message, field, j, &tmp_eval_results)) {
        do_not_have_result = false;
        AddEvalResult(field->name(), expected,
                      tmp_eval_results[0].actual + "\n" +
                      "at_index: " + SimpleXtoa(j), false, eval_results);
      }
    }
    if (do_not_have_result) {
      AddEvalResult(field->name(), expected,
                    "value not found", true, eval_results);
    }
    result = result && do_not_have_result;
  }
  return result;
}

bool Evaluate(const RepeatedMessageExpectation& expectation,
              const Message& message,
              const FieldDescriptor* field,
              vector<EvaluationResult>* eval_results) {
  const Reflection* reflection = message.GetReflection();
  const int field_size = reflection->FieldSize(message, field);
  vector<EvaluationResult> tmp_eval_results;
  bool result = true;
  if (expectation.has_for_each()) {
    const string expected = "for_each: " +
        DebugString(expectation.for_each().message_expectation());
    bool for_each_result = true;
    if (field_size == 0) {
      AddEvalResult(field->name(), expected,
                    "no message in the repeated field", true, eval_results);
    } else {
      for (int i = 0; i < field_size; ++i) {
        tmp_eval_results.clear();
        if (!Evaluate(expectation.for_each().message_expectation(),
                      reflection->GetRepeatedMessage(message, field, i),
                      &tmp_eval_results)) {
          for_each_result = false;
          string actual = "failed at_index: " + SimpleXtoa(i);
          for (int j = 0; j < tmp_eval_results.size(); ++j) {
            if (!tmp_eval_results[j].success) {
              actual += "\nfailure on field: " + tmp_eval_results[j].field +
                  "\nactual: " + tmp_eval_results[j].actual;
            }
          }
          AddEvalResult(field->name(), expected, actual, false, eval_results);
        }
      }
      if (for_each_result) {
        AddEvalResult(field->name(), expected,
                      "all good", true, eval_results);
      }
    }
    result = result && for_each_result;
  }
  for (int i = 0; i < expectation.have_size(); ++i) {
    string expected = "have: " +
        DebugString(expectation.have(i).message_expectation());
    if (expectation.have(i).has_at_index()) {
      expected += "at_index: " + DebugString(expectation.have(i).at_index());
    }
    bool have_result = false;
    for (int j = 0; j < field_size; ++j) {
      tmp_eval_results.clear();
      if (Evaluate(expectation.have(i).message_expectation(),
                   reflection->GetRepeatedMessage(message, field, j),
                   &tmp_eval_results)) {
        if (expectation.have(i).has_at_index()) {
          if (Evaluate(expectation.have(i).at_index(), static_cast<int32>(j),
                       NULL)) {
            have_result = true;
            string actual = "found at_index: " + SimpleXtoa(j);
            for (int k = 0; k < tmp_eval_results.size(); ++k) {
              if (tmp_eval_results[k].success) {
                actual += "\nsuccess on field: " + tmp_eval_results[k].field +
                    "\nactual: " + tmp_eval_results[k].actual;
              }
            }
            AddEvalResult(field->name(), expected, actual, true, eval_results);
          }
        } else {
          have_result = true;
          string actual = "found at_index: " + SimpleXtoa(j);
          for (int k = 0; k < tmp_eval_results.size(); ++k) {
            if (tmp_eval_results[k].success) {
              actual += "\nsuccess on field: " + tmp_eval_results[k].field +
                  "\nactual: " + tmp_eval_results[k].actual + "\n";
            }
          }
          AddEvalResult(field->name(), expected, actual, true, eval_results);
        }
      }
    }
    if (!have_result) {
      AddEvalResult(field->name(), expected,
                    string("message not found") +
                    (expectation.have(i).has_at_index() ? " at the index" : ""),
                    false, eval_results);
    }
    result = result && have_result;
  }
  for (int i = 0; i < expectation.do_not_have_size(); ++i) {
    const string expected = "do_not_have: " +
        DebugString(expectation.do_not_have(i).message_expectation());
    bool do_not_have_result = true;
    for (int j = 0; j < field_size; ++j) {
      tmp_eval_results.clear();
      if (Evaluate(expectation.do_not_have(i).message_expectation(),
                   reflection->GetRepeatedMessage(message, field, j),
                   &tmp_eval_results)) {
        do_not_have_result = false;
        string actual = "found at_index: " + SimpleXtoa(j);
        for (int k = 0; k < tmp_eval_results.size(); ++k) {
          if (tmp_eval_results[k].success) {
            actual += "\nsuccess on field: " + tmp_eval_results[k].field +
                "\nactual: " + tmp_eval_results[k].actual;
          }
        }
        AddEvalResult(field->name(), expected, actual, false, eval_results);
      }
    }
    if (do_not_have_result) {
      AddEvalResult(field->name(), expected,
                    "message not found", true, eval_results);
    }
    result = result && do_not_have_result;
  }
  return result;
}

}  // namespace protobuf_expectation
}  // namespace google
